package com.fms.service.impl;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fms.constant.MessageConstant;
import com.fms.constant.Reference;
import com.fms.constant.Role;
import com.fms.dto.JsonRequest;
import com.fms.entity.*;
import com.fms.exception.ServiceException;
import com.fms.mapper.*;
import com.fms.service.AttendanceService;
import com.fms.service.SysDeptService;
import com.fms.util.CastUtils;
import com.fms.vo.AttendanceInfoVO;
import com.fms.vo.AttendanceNumberVo;
import com.mysql.cj.util.StringUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.*;

@Service
@RequiredArgsConstructor
@Slf4j
public class AttendanceServiceImpl extends ServiceImpl<AttendanceMapper, Attendance> implements AttendanceService {
    private final AttendanceMapper attendanceMapper;
    private final AttendanceTaskMapper attendanceTaskMapper;
    private final TaskDeptMapper taskDeptMapper;
    private final SysUserMapper sysUserMapper;
    private final SysDeptMapper sysDeptMapper;
    private final SysDeptService sysDeptService;
    @Override
    public AttendanceInfoVO getRecentAttendanceOfUser(String username) {
        //用户签到信息
        Attendance attendance = attendanceMapper.selectOne(new LambdaQueryWrapper<Attendance>()
                .eq(Attendance::getUsername, username)
                .ne(Attendance::getStatus, 5));
        if(attendance==null){//没有任务进行
            //查找身份
            Long deptId = sysUserMapper
                    .selectOne(new LambdaQueryWrapper<SysUser>().eq(SysUser::getUsername, username))
                    .getDeptId();
            //查找任务
            List<TaskDept> taskDeptList = taskDeptMapper.selectList(new LambdaQueryWrapper<TaskDept>().eq(TaskDept::getDeptId, deptId));
            if(taskDeptList==null||taskDeptList.isEmpty()){
                return null;
            }
            //获取最近的nextStartTime
            List<Long> taskIds = taskDeptList.stream().map(TaskDept::getTaskId).toList();
            LocalDateTime nextDateTime=LocalDateTime.MAX;
            AttendanceTask nextAttendanceTask=null;
            for(Long id:taskIds){
                AttendanceTask attendanceTask = attendanceTaskMapper.selectOne(new LambdaQueryWrapper<AttendanceTask>()
                        .eq(AttendanceTask::getId, id)
                        .ne(AttendanceTask::getStatus,0));
                LocalDateTime nextStartTime;
                List<Integer> weeks = CastUtils.castStringToWeeks(attendanceTask.getWeekDaysString());
                LocalTime startTime = attendanceTask.getStartTime();
                LocalDate now = LocalDate.now();
                //计算nextStartTime
                if(startTime.isBefore(LocalTime.now())){
                    nextStartTime=LocalDateTime
                            .of(CastUtils.castDispatchDate(now.plusDays(1),weeks)
                                    , startTime);
                }else{
                    nextStartTime=LocalDateTime
                            .of(CastUtils.castDispatchDate(now,weeks)
                                    , startTime);
                }
                //比较最近的nextStartTime
                if(nextStartTime.isBefore(nextDateTime)){
                    nextDateTime=nextStartTime;
                    nextAttendanceTask=attendanceTask;
                }
            }
            //返回结果
            if(!(nextAttendanceTask ==null)){
                return AttendanceInfoVO.builder()
                        .taskId(nextAttendanceTask.getId())
                        .username(username)
                        .longitude(nextAttendanceTask.getLongitude())
                        .latitude(nextAttendanceTask.getLatitude())
                        .nextStartTime(nextDateTime)
                        .build();
            }else{
                return null;
            }
        }else{//有任务正在进行
            Long taskId=attendance.getTaskId();
            AttendanceTask attendanceTask = attendanceTaskMapper.selectById(taskId);
            Map<String, LocalDateTime> localDateTimeMap = CastUtils.castLocalDateTime(attendance.getDate(),
                    attendanceTask.getStartTime(), attendanceTask.getInDeadline(), attendanceTask.getOutDeadline());
            //返回vo
            return AttendanceInfoVO.builder()
                    .id(attendance.getId())
                    .taskId(taskId)
                    .username(username)
                    .inImageUrl(attendance.getInImgUrl())
                    .outImageUrl(attendance.getOutImgUrl())
                    .inTime(attendance.getInTime())
                    .outTime(attendance.getOutTime())
                    .status(attendance.getStatus())
                    .outLongitude(attendance.getOutLongitude())
                    .outLatitude(attendance.getOutLatitude())
                    .inLongitude(attendance.getInLongitude())
                    .inLatitude(attendance.getInLatitude())
                    .inDeadline(localDateTimeMap.get(MessageConstant.IN_DATE_TIME))
                    .outDeadline(localDateTimeMap.get(MessageConstant.OUT_DATE_TIME))
                    .startTime(localDateTimeMap.get(MessageConstant.STAR_DATE_TIME))
                    .longitude(attendanceTask.getLongitude())
                    .latitude(attendanceTask.getLatitude())
                    .weekDays(CastUtils.castStringToWeeks(attendanceTask.getWeekDaysString()))
                    .build();
        }
    }
    @Override
    public Page<AttendanceInfoVO> getAllAttendance(JsonRequest.AttendancePageRequest attendancePageRequest) {
        List<AttendanceInfoVO> list=new ArrayList<>();
        String username = attendancePageRequest.getUsername();
        Long deptId = attendancePageRequest.getDeptId();
        //用户签到信息
        List<String> usernameList =null;
        if(deptId!=null){
            usernameList = sysUserMapper.selectList(new LambdaQueryWrapper<SysUser>()
                    .eq(SysUser::getDeptId, deptId))
                    .stream()
                    .map(SysUser::getUsername)
                    .toList();
        }
        //分页查询具体任务
        Page<Attendance> pageInfo=new Page<>(attendancePageRequest.getPage()
                ,attendancePageRequest.getPageSize()
                ,true);
        LambdaQueryWrapper<Attendance> lqw=new LambdaQueryWrapper<>();
        lqw.eq(attendancePageRequest.getDate()!=null, Attendance::getDate,attendancePageRequest.getDate())
                .eq(attendancePageRequest.getStatus()!=null,Attendance::getStatus,attendancePageRequest.getStatus())
                .like(!StringUtils.isNullOrEmpty(username),Attendance::getUsername,username)
                .in(usernameList!=null&&!usernameList.isEmpty(),Attendance::getUsername,usernameList)
                .orderByDesc(Attendance::getDate)
                .orderByAsc(Attendance::getInTime);
        Page<Attendance> originalPage = this.page(pageInfo,lqw);
        List<Attendance> records = originalPage.getRecords();
        for(Attendance attendance : records) {
            Long taskId = attendance.getTaskId();
            //查询关联抽象任务
            AttendanceTask attendanceTask = attendanceTaskMapper.getIgnoreById(taskId);
            if(attendancePageRequest.getStartTime()!=null && !attendancePageRequest.getStartTime().equals(attendanceTask.getStartTime())){
                continue;
            }
            //计算具体时间
            Map<String, LocalDateTime> localDateTimeMap = CastUtils.castLocalDateTime(attendance.getDate()
                    , attendanceTask.getStartTime()
                    , attendanceTask.getInDeadline()
                    , attendanceTask.getOutDeadline());
            //获取人员
            SysUser sysUser = sysUserMapper.selectOne(new LambdaQueryWrapper<SysUser>()
                    .eq(SysUser::getUsername, attendance.getUsername()));
            list.add(AttendanceInfoVO.builder()
                    .taskId(taskId)
                    .id(attendance.getId())
                    .username(attendance.getUsername())
                    .inImageUrl(attendance.getInImgUrl())
                    .outImageUrl(attendance.getOutImgUrl())
                    .inTime(attendance.getInTime())
                    .outTime(attendance.getOutTime())
                    .status(attendance.getStatus())
                    .outLongitude(attendance.getOutLongitude())
                    .outLatitude(attendance.getOutLatitude())
                    .inLongitude(attendance.getInLongitude())
                    .inLatitude(attendance.getInLatitude())
                    .inDeadline(localDateTimeMap.get(MessageConstant.IN_DATE_TIME))
                    .outDeadline(localDateTimeMap.get(MessageConstant.OUT_DATE_TIME))
                    .startTime(localDateTimeMap.get(MessageConstant.STAR_DATE_TIME))
                    .longitude(attendanceTask.getLongitude())
                    .latitude(attendanceTask.getLatitude())
                    .weekDays(CastUtils.castStringToWeeks(attendanceTask.getWeekDaysString()))
                    .realName(sysUser==null?null:sysUser.getRealName())
                            .deptName(sysUser==null?null:sysDeptMapper.selectById(sysUser.getDeptId()).getName())
                    .build());
        }
        //返回拓展的分页数据
        Page<AttendanceInfoVO> page = new Page<>(originalPage.getCurrent(), originalPage.getSize());
        page.setTotal(originalPage.getTotal());
        page.setRecords(list);
        page.setPages(originalPage.getPages());
        return page;
    }


    @Override
    @Transactional
    public void attendedOfAttendance(JsonRequest.AttendedOfAttendanceRequest attendedOfAttendanceRequest) {
        String imgUrl = attendedOfAttendanceRequest.getImgUrl();
        Double attendedLongitude = attendedOfAttendanceRequest.getAttendedLongitude();
        Double attendedLatitude = attendedOfAttendanceRequest.getAttendedLatitude();
        Long id = attendedOfAttendanceRequest.getId();
        LocalDateTime now = LocalDateTime.now();
        Integer type = attendedOfAttendanceRequest.getType();
        //数据存入数据库
        switch (type){
            case 1,2:
                int status=2;
                if(type==2){
                    status=3;
                }
                if(!this.lambdaUpdate()
                        .set(imgUrl!=null, Attendance::getInImgUrl,imgUrl)
                        .set(attendedLatitude!=null,Attendance::getInLatitude,attendedLatitude)
                        .set(attendedLongitude!=null,Attendance::getInLongitude,attendedLongitude)
                        .set(Attendance::getInTime, now)
                        .set(Attendance::getStatus,status)
                        .eq(Attendance::getId,id)
                        .update()){
                    throw new ServiceException("签到失败");
                }
                break;
            case 3:
                if(!this.lambdaUpdate()
                        .set(imgUrl!=null, Attendance::getOutImgUrl,imgUrl)
                        .set(attendedLatitude!=null,Attendance::getOutLatitude,attendedLatitude)
                        .set(attendedLongitude!=null,Attendance::getOutLongitude,attendedLongitude)
                        .set(Attendance::getOutTime, now)
                        .set(Attendance::getStatus,5)
                        .eq(Attendance::getId,id)
                        .update()){
                    throw new ServiceException("签退失败");
                }
                break;
        }
    }

    @Override
    public List<AttendanceNumberVo> getNumber(JsonRequest.AttendanceNumberRequest attendanceNumberRequest) {
        Integer maxHoliday = attendanceNumberRequest.getMaxHoliday();
        Integer monthNumber = attendanceNumberRequest.getMonthNumber();
        LocalDate minStartDate = attendanceNumberRequest.getStartDate();
        LocalDate now = LocalDate.now();
        List<AttendanceNumberVo> list = new ArrayList<>();
        //过滤角色
        List<String> noAttendedList = List.of(
                Reference.ROLE_MAP.inverse().get(Role.ADMIN),
                Reference.ROLE_MAP.inverse().get(Role.MANAGER),
                Reference.ROLE_MAP.inverse().get(Role.GENERAL_MANAGER)
        );
        List<Long> noAttendedIdList = sysDeptService.lambdaQuery().select(SysDept::getId)
                .in(SysDept::getName, noAttendedList)
                .list().stream().map(SysDept::getId).toList();
        //查找符合条件用户
        List<SysUser> sysUsers = sysUserMapper.selectList(
                new LambdaQueryWrapper<SysUser>()
                        .eq(SysUser::getStatus, 1)
                        .notIn(SysUser::getDeptId, noAttendedIdList)
                        .eq(attendanceNumberRequest.getDeptId()!=null
                                ,SysUser::getDeptId
                                , attendanceNumberRequest.getDeptId())
                        .like(StrUtil.isNotBlank(attendanceNumberRequest.getName())
                                ,SysUser::getRealName
                                , attendanceNumberRequest.getName())
        );

        //统计每个用户考勤情况
        sysUsers.forEach(sysUser -> {
            List<AttendanceNumberVo.AbsenteeismNote> listNote=new ArrayList<>();
            String realName = sysUser.getRealName();
            //获取所有记录
            List<Attendance> attendanceList = this.lambdaQuery()
                    .eq(Attendance::getUsername, sysUser.getUsername())
                    .eq(Attendance::getStatus, 5)
                    .ge(Attendance::getDate, minStartDate)
                    .list();
            long absenteeismNumberSum = 0;
            long lateNumberSum = 0;
            long earlyNumberSum = 0;
            LocalDate startDate = minStartDate;
            boolean normalFlag=true;
            for(int i = 1; i<=monthNumber; i++){
                LocalDate  endDate =minStartDate.plusMonths(i).isAfter(now)? now : minStartDate.plusMonths(i);
                //过滤掉不在当前月份的记录
                LocalDate finalStartDate = startDate;
                List<Attendance> attendances = attendanceList.stream()
                        .filter(attendance -> (attendance.getDate().equals(finalStartDate)
                                || attendance.getDate().isAfter(finalStartDate))
                                && attendance.getDate().isBefore(endDate)
                        )
                        .sorted(Comparator.comparing(Attendance::getDate))
                        .toList();
                log.info("{}，{}到{}的记录有：{}",realName, startDate, endDate,attendances.size());

                //当前月的缺勤数
                long absenteeismNumber = attendances.stream()
                        .filter(attendance -> attendance.getInTime() == null)
                        .map(Attendance::getDate)
                        .distinct()
                        .count();
                absenteeismNumberSum += absenteeismNumber;
                log.info("获取{},{}到{}的缺勤数:{}", realName, startDate, endDate, absenteeismNumber);
                //检验是否异常
                if(normalFlag){
                    normalFlag= absenteeismNumber <= maxHoliday;
                }
                //当前月的迟到数
                long lateNumber = attendances.stream()
                        .filter(attendance -> {
                            Long taskId = attendance.getTaskId();
                            LocalTime inDeadline = attendanceTaskMapper.getIgnoreById(taskId).getInDeadline();
                            LocalDateTime inDeadlineDataTime= LocalDateTime.of(attendance.getDate(), inDeadline);
                            LocalDateTime inTime = attendance.getInTime();
                            return inTime != null && inTime.isAfter(inDeadlineDataTime);
                        })
                        .map(Attendance::getDate)
                        .distinct()
                        .count();
                lateNumberSum+=lateNumber;
                log.info("获取{},{}到{}的迟到数:{}", realName, startDate, endDate, lateNumber);
                //当前月的早退数
                long earlyNumber = attendances.stream()
                        .filter(attendance -> attendance.getOutTime()==null&&attendance.getInTime()!=null)
                        .map(Attendance::getDate)
                        .distinct()
                        .count();
                earlyNumberSum+=earlyNumber;
                log.info("获取{},{}到{}的早退数:{}", realName, startDate, endDate, earlyNumber);
                //添加缺勤记录
                listNote.add(AttendanceNumberVo.AbsenteeismNote.builder()
                        .year(startDate.getYear())
                        .month(startDate.getMonthValue())
                        .maxHoliday(maxHoliday)
                        .absenteeismNumber((int)absenteeismNumber)
                        .earlyNumber((int)earlyNumber)
                        .lateNumber((int) lateNumber)
                        .build()
                );
                if(endDate==now){
                    break;
                }else{
                    startDate=endDate;
                }
            }
            list.add(AttendanceNumberVo.builder()
                    .name(realName)
                    .phone(sysUser.getPhone())
                    .deptName(sysDeptMapper.selectById(sysUser.getDeptId()).getName())
                    .absenteeismNumber((int)absenteeismNumberSum)
                    .lateNumber((int)lateNumberSum)
                    .earlyNumber((int)earlyNumberSum)
                    .isNormal(normalFlag)
                    .note(listNote)
                    .build()
            );
        });
        //按缺勤降序排序
        return list.stream().sorted((o1,o2)->{
            if(!Objects.equals(o1.getAbsenteeismNumber(), o2.getAbsenteeismNumber())){
                return o2.getAbsenteeismNumber()-o1.getAbsenteeismNumber();
            }else if(!Objects.equals(o1.getLateNumber(), o2.getLateNumber())){
                return o2.getLateNumber()-o1.getLateNumber();
            }else if(!Objects.equals(o1.getEarlyNumber(), o2.getEarlyNumber())){
                return o2.getEarlyNumber()-o1.getEarlyNumber();
            }
            return 0;
        }).toList();
    }
}
